package de.herzog.bbp.view.javafx;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javafx.beans.value.ChangeListener;
import javafx.scene.control.TableCell;
import javafx.scene.control.TextField;
import de.herzog.bbp.view.bean.AbstractBean;

public class EditingTableCell<S extends AbstractBean, T> extends TableCell<S, T> {

	private static final Logger LOG = LoggerFactory.getLogger(EditingTableCell.class);

	private Validator validator;
	private AbstractStringConverter<S, T> converter;
	private AutoCompleteListFactory<S, T> autoCompleteListFactory;
	private TextField textField;
	private ChangeListener<? super Boolean> commitOnLoseFocusListener;
	private EditNextColumnOnTabHandler<S, T> editNextColumnOnTabHandler;
	private AutoCompleteHandler<T> autoCompleteHandler;

	public EditingTableCell(Validator validator, AbstractStringConverter<S, T> converter,
			AutoCompleteListFactory<S, T> autoCompleteListFactory) {
		this.validator = validator;
		this.converter = converter;
		this.autoCompleteListFactory = autoCompleteListFactory;
		this.getStyleClass().add("text-field-table-cell");
	}

	private ChangeListener<? super Boolean> getCommitOnLoseFocusListener() {
		if (commitOnLoseFocusListener == null) {
			commitOnLoseFocusListener = (observable, oldFocus, newFocus) -> {
				if (oldFocus && !newFocus) {
					commitEdit(getItemObject());
				}
			};
		}
		return commitOnLoseFocusListener;
	}

	private EditNextColumnOnTabHandler<S, T> getEditNextColumnOnTabHandler() {
		// Has to be initialized lazily because at constructor time, the table
		// view is still null
		if (editNextColumnOnTabHandler == null) {
			editNextColumnOnTabHandler = new EditNextColumnOnTabHandler<>(getTableView(), 1);
		}
		return editNextColumnOnTabHandler;
	}

	private AutoCompleteHandler<T> getAutoCompleteHandler() {
		if (/* autoCompleteHandler == null && */autoCompleteListFactory != null) {
			autoCompleteHandler = new AutoCompleteHandler<T>(textField, autoCompleteListFactory.getList());
			LOG.trace("Created AutoCompleteHandler for column " + getTableColumn().getId());
		}
		return autoCompleteHandler;
	}

	@Override
	public void startEdit() {
		if (!isEditable() || !getTableView().isEditable() || !getTableColumn().isEditable()) {
			return;
		}
		super.startEdit();

		if (isEditing()) {
			LOG.trace("Starting edit");
			updateTableCellInHelpers();
			if (textField == null) {
				LOG.trace("Creating textfield");
				textField = TableCellUtils.createTextField(this, converter, validator, autoCompleteListFactory);
				textField.focusedProperty().addListener(getCommitOnLoseFocusListener());
				textField.setOnKeyPressed(getEditNextColumnOnTabHandler());
				if (autoCompleteListFactory != null) {
					textField.setOnKeyReleased(getAutoCompleteHandler());
				}
			}
			TableCellUtils.startEdit(this, converter, textField);
		}
	}

	private void updateTableCellInHelpers() {
		if (converter != null) {
			converter.setTableCell(this);
		}
		if (autoCompleteListFactory != null) {
			autoCompleteListFactory.setTableCell(this);
			if (textField != null) {
				autoCompleteHandler = new AutoCompleteHandler<T>(textField, autoCompleteListFactory.getList());
			}
		}
		getEditNextColumnOnTabHandler().setTableCell(this);
	}

	@Override
	public void cancelEdit() {
		super.cancelEdit();
		TableCellUtils.cancelEdit(this, converter, null);
	}

	@Override
	public void updateItem(T item, boolean empty) {
		super.updateItem(item, empty);
		TableCellUtils.updateItem(this, converter, validator, textField);
	}

	T getItemObject() {
		if (converter == null) {
			@SuppressWarnings("unchecked")
			T object = (T) textField.getText();
			return object;
		} else {
			return converter.fromString(textField.getText());
		}
	}
}
